
#include <BStream>
#include <BHtmlItem>
#include <BXmlNode>
#include "member_BXmlNode.h"

using namespace BWE;

#define member					(*(member_BXmlNode*)_ptr)
#define member_allocate()		_ptr = new member_BXmlNode(this)
#define member_release()		delete (member_BXmlNode*)_ptr

BXmlNode::BXmlNode(BXmlNode* parent)
{
	member_allocate();
	setParent(parent);
}
BXmlNode::BXmlNode(const BString& name, BXmlNode* parent) : BObject(name)
{
	member_allocate();
	setParent(parent);
}
BXmlNode::~BXmlNode()
{
	member_release();
}

bool BXmlNode::empty() const
{
	return member.text.empty() && member.attribSet.empty() && member.children.empty();
}

bool BXmlNode::setFormat(const BString& format)
{
	if (member.format.text != format)
	{
		member.format.fromString(format);
		return true;
	}
	return true;
}
const BString& BXmlNode::format() const
{
	return member.format.text;
}

bool BXmlNode::setText(const BString& text)
{
	if (member.text != text)
	{
		member.text = text;
		member.dirty = true;
	}
	return true;
}
const BString& BXmlNode::text() const
{
	return member.text;
}

bool BXmlNode::setAttrib(const BString& name, const BString& value)
{
	if (name.empty())
		return false;
	member.attribSet[name] = value;
	return true;
}
const BString& BXmlNode::attrib(const BString& name) const
{
	return member.attribSet(name);

}
bool BXmlNode::hasAttrib(const BString& name) const
{
	return name.contain(name);
}

void BXmlNode::setAttribSet(const BMap<BString, BString>& attribSet)
{
	member.attribSet = attribSet;
}
const BMap<BString, BString>& BXmlNode::attribSet() const
{
	return member.attribSet;
}

bool BXmlNode::removeAttrib(const BString& name)
{
	return member.attribSet.remove(name);
}

void BXmlNode::clearAttribs()
{
	member.attribSet.clear();
}

const BXmlNode* BXmlNode::root() const
{
	const BXmlNode* node = this;
	while (xml_member(node)->parent)
		node = xml_member(node)->parent;
	return node;
}
BXmlNode* BXmlNode::root()
{
	BXmlNode* node = this;
	while (xml_member(node)->parent)
		node = xml_member(node)->parent;
	return node;
}

void BXmlNode::setParent(BXmlNode* parent)
{
	if (parent && parent != this)
	{
		parent->addChild(this);
	}
}

BXmlNode* BXmlNode::parent()
{
	return member.parent;
}
const BXmlNode* BXmlNode::parent() const
{
	return member.parent;
}

BXmlNode* BXmlNode::parent(const BString& name)
{
	if (name.empty())
		return 0;
	BXmlNode* parent = member.parent;
	while (parent && parent->name() != name)
		parent = xml_member(parent)->parent;
	return parent;
}
const BXmlNode* BXmlNode::parent(const BString& name) const
{
	if (name.empty())
		return 0;
	const BXmlNode* parent = member.parent;
	while (parent && parent->name() != name)
		parent = xml_member(parent)->parent;
	return parent;
}

BXmlNode* BXmlNode::addText(const BString& text)
{
	BXmlNode* textNode = new BXmlNode();
	textNode->setText(text);

	member.children.append(textNode);
	xml_member(textNode)->parent = this;
	member.dirty = true;
	return textNode;
}
BXmlNode* BXmlNode::addChild(const BString& name, const BString& text)
{
	BXmlNode* child = new BXmlNode(name);
	child->setText(text);
	member.children.append(child);
	xml_member(child)->parent = this;
	member.dirty = true;
	return child;
}
bool BXmlNode::addChild(BXmlNode* child)
{
	if (!child)
		return false;
	if (child == this)
		return false;
	if (child == member.parent)
		return false;
	if (!member.children.contain(child))
	{
		member.children.append(child);
		if (xml_member(child)->parent)
			xml_member(child)->parent->remove(child);
		xml_member(child)->parent = this;
		member.dirty = true;
		return true;
	}
	return false;
}
bool BXmlNode::setChild(int pos, BXmlNode* child)
{
	if (pos >= 0 && pos < member.children.size())
	{
		member.children[pos] = child;
		return true;
	}
	return false;
}
bool BXmlNode::insertChild(int pos, BXmlNode* node)
{
	if (!node)
		return false;
	if (node == this)
		return false;
	if (node == member.parent)
		return false;
	if (member.children.insert(pos, node))
	{
		if (xml_member(node)->parent)
			xml_member(node)->parent->removeChild(node);
		xml_member(node)->parent = this;
		return true;
	}
	return false;
}
bool BXmlNode::removeChild(const BXmlNode* node)
{
	if (!node)
		return false;
	if (node == this)
		return false;
	if (node == member.parent)
		return false;
	if (xml_member(node)->parent == this)
	{
		xml_member(node)->parent = 0;
		member.children.remove(node);
		return true;
	}
	return false;
}

int BXmlNode::index() const
{
	if (member.parent)
	{
		BArray<BXmlNodeHolder>& up_children = xml_member(member.parent)->children;
		for (int i = 0; i < up_children.size(); i++)
		{
			if (up_children[i] == this)
				return i;
		}
	}
	return -1;
}
int BXmlNode::level() const
{
	int level = 0;
	const BXmlNode* node = this->parent();
	while (node)
	{
		level++;
		node = xml_member(node)->parent;
	}
	return level;
}

const BString& BXmlNode::plainText() const
{
	if (member.dirty)
	{
		member.plainText.reset();
		member.plainText.append(member.text);
		for (int i = 0; i < member.children.size(); i++)
		{
			const BString& text = member.children[i]->plainText();
			member.plainText.append(text);
		}
		member.dirty = false;
	}
	return member.plainText;
}

void BXmlNode::remove(BXmlNode* child)
{
	if (child && member.children.contain(child))
	{
		xml_member(child)->parent = 0;
		member.children.remove(child);
		member.dirty = true;
	}
}
void BXmlNode::remove(const BString& desc)
{
	if (desc.contain(':') || desc.contain('&') || desc.contain('|'))
	{
		XmlFilter filter(desc);
		for (int i = 0; i < member.children.size(); i++)
		{
			BXmlNode* child = member.children[i];
			if (filter(child))
			{
				xml_member(child)->parent = 0;
				member.children.remove(i--);
				member.dirty = true;
			}
		}
	}
	else
	{
		for (int i = 0; i < member.children.size(); i++)
		{
			BXmlNode* child = member.children[i];
			if (child->name() == desc)
			{
				xml_member(child)->parent = 0;
				member.children.remove(i--);
				member.dirty = true;
			}
		}
	}
}
void BXmlNode::clear()
{
	for (int i = 0; i < member.children.size(); i++)
	{
		BXmlNode* child = member.children[i];
		xml_member(child)->parent = 0;
	}
	member.children.clear();
	member.plainText.clear();
	member.dirty = true;
}

int BXmlNode::childCount() const
{
	return member.children.size();
}
BXmlNode* BXmlNode::child(int index)
{
	return member.children(index);
}
const BXmlNode* BXmlNode::child(int index) const
{
	return member.children(index);
}

BXmlNode* BXmlNode::child(const BString& desc)
{
	if (desc.contain(':') || desc.contain('&') || desc.contain('|'))
	{
		XmlFilter filter(desc);
		for (int i = 0; i < member.children.size(); i++)
		{
			BXmlNode* child = member.children[i];
			if (filter(child))
				return child;
		}
	}
	else
	{
		for (int i = 0; i < member.children.size(); i++)
		{
			BXmlNode* child = member.children[i];
			if (child->name() == desc)
				return child;
		}
	}
	return 0;
}
const BXmlNode* BXmlNode::child(const BString& desc) const
{
	if (desc.contain(':') || desc.contain('&') || desc.contain('|'))
	{
		XmlFilter filter(desc);
		for (int i = 0; i < member.children.size(); i++)
		{
			BXmlNode* child = member.children[i];
			if (filter(child))
				return child;
		}
	}
	else
	{
		for (int i = 0; i < member.children.size(); i++)
		{
			BXmlNode* child = member.children[i];
			if (child->name() == desc)
				return child;
		}
	}
	return 0;
}

BXmlNode* BXmlNode::find(const BString& desc)
{
	if (desc.contain(':') || desc.contain('&') || desc.contain('|'))
	{
		XmlFilter filter(desc);
		BXmlNode* node = member.find(filter);
		return node;
	}
	else
	{
		if (this->name() == desc)
			return this;
		for (int i = 0; i < member.children.size(); i++)
		{
			BXmlNode* child = member.children[i];
			BXmlNode* node = child->find(desc);
			if (node)
				return node;
		}
	}
	return 0;
}
const BXmlNode* BXmlNode::find(const BString& desc) const
{
	if (desc.contain(':') || desc.contain('&') || desc.contain('|'))
	{
		XmlFilter filter(desc);
		for (int i = 0; i < member.children.size(); i++)
		{
			BXmlNode* child = member.children[i];
			BXmlNode* node = child->find(desc);
			if (node)
				return node;
		}
	}
	else
	{
		if (this->name() == desc)
			return this;
		for (int i = 0; i < member.children.size(); i++)
		{
			BXmlNode* child = member.children[i];
			BXmlNode* node = child->find(desc);
			if (node)
				return node;
		}
	}
	return 0;
}

BXmlNode* BXmlNode::prev()
{
	if (member.parent)
	{
		BArray<BXmlNodeHolder>& up_children = xml_member(member.parent)->children;
		int index = up_children.find(this);
		if (index > 1)
			return up_children[index - 1];
	}
	return 0;
}
BXmlNode* BXmlNode::next()
{
	if (member.parent)
	{
		BArray<BXmlNodeHolder>& up_children = xml_member(member.parent)->children;
		int index = up_children.find(this);
		if (index < up_children.size() - 1)
			return up_children[index + 1];
	}
	return 0;
}

bool BXmlNode::above(const BXmlNode* node) const
{
	if (!node)
		return false;
	BXmlNode* parent = xml_member(node)->parent;
	while (parent)
	{
		if (parent == this)
			return true;
		parent = xml_member(parent)->parent;
	}
	return false;
}
bool BXmlNode::under(const BXmlNode* node) const
{
	BXmlNode* parent = member.parent;
	while (parent)
	{
		if (parent == node)
			return true;
		parent = xml_member(parent)->parent;
	}
	return false;
}
bool BXmlNode::contain(const BXmlNode* node) const
{
	if (!node)
		return false;
	for (int i = 0; i < member.children.size(); i++)
	{
		if (member.children[i] == node)
			return true;
	}
	return false;
}

BXmlNode* BXmlNode::locate(const BString& desc)
{
	BStringList namelst = desc.split('/');
	BXmlNode* node = this->child(namelst.first());
	namelst.remove(0);
	while (node && namelst.size())
	{
		const BString& text = namelst.first();
		node = node->child(text);
		namelst.remove(0);
	}
	return node;
}
const BXmlNode* BXmlNode::locate(const BString& desc) const
{
	BStringList namelst = desc.split('/');
	const BXmlNode* node = this->child(namelst.first());
	namelst.remove(0);
	while (node && namelst.size())
	{
		const BString& text = namelst.first();
		node = node->child(text);
		namelst.remove(0);
	}
	return node;
}

BString BXmlNode::path(const char* split) const
{
	BString text = this->name();
	const BXmlNode* node = this->parent();
	while (node)
	{
		text.prepend(split);
		text.prepend(node->name());
		node = node->parent();
	}
	return text;
}
BString BXmlNode::path(const BString& split) const
{
	BString text = this->name();
	const BXmlNode* node = this->parent();
	while (node)
	{
		text.prepend(split);
		text.prepend(node->name());
		node = node->parent();
	}
	return text;
}

BString BXmlNode::pathTo(const BXmlNode* parent, const char* split) const
{
	if (this == parent)
		return BString();
	if (parent && !this->under(parent))
		return BString();
	BString text = this->name();
	const BXmlNode* node = this->parent();
	while (node && node != parent)
	{
		text.prepend(split);
		text.prepend(node->name());
		node = node->parent();
	}
	return text;
}
BString BXmlNode::pathTo(const BXmlNode* parent, const BString& split) const
{
	if (this == parent)
		return BString();
	if (parent && !this->under(parent))
		return BString();
	BString text = this->name();
	const BXmlNode* node = this->parent();
	while (node && node != parent)
	{
		text.prepend(split);
		text.prepend(node->name());
		node = node->parent();
	}
	return text;
}
